﻿#include "FrameBuffer.h"

/////////////////////////////////////////////////////
// UFrameBuffer

UFrameBuffer::UFrameBuffer(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	Width = -1;
	Height = -1;

	Texture = nullptr;
}

void UFrameBuffer::Resize(int32 InWidth, int32 InHeight)
{
	if (Width != InWidth || Height != InHeight)
	{
		Width = FMath::Max(2, InWidth);
		Height = FMath::Max(2, InHeight);
		
		Pixels.SetNum(Width * Height);

		// 所有数据初始化为0，也就是纯黑色
		FMemory::Memzero(Pixels.GetData(), Pixels.Num());
	}
}

void UFrameBuffer::Clear(FLinearColor ClearColor)
{
	if (Pixels.Num() > 0)
	{
		const uint32 Color = ClearColor.ToFColor(false).DWColor();
		for (int32 Index = 0, Count = Pixels.Num(); Index < Count; ++Index)
		{
			Pixels[Index] = Color;
		}
	}
}

void UFrameBuffer::Point(int32 X, int32 Y, FLinearColor Color)
{
	if (X >= 0 && X < Width && Y >= 0 && Y < Height)
	{
		Pixels[Y * Width + X] = Color.ToFColor(false).DWColor();
	}
}

UTexture2D* UFrameBuffer::UpdateTexture2D()
{
	if (Width <= 0 || Height <= 0)
		return nullptr;
	
	if (!IsValid(Texture))
	{
		Texture = NewObject<UTexture2D>(
			GetTransientPackage(),
			NAME_None,
			RF_Transient
			);

		Texture->SRGB = false;
		Texture->LODGroup = TextureGroup::TEXTUREGROUP_UI;
		Texture->MipLoadOptions = ETextureMipLoadOptions::OnlyFirstMip;
		Texture->NeverStream = true;

		Texture->PlatformData = new FTexturePlatformData();
		Texture->PlatformData->SizeX = Width;
		Texture->PlatformData->SizeY = Height;
		Texture->PlatformData->PixelFormat = EPixelFormat::PF_B8G8R8A8;
		
		FTexture2DMipMap* Mip = new FTexture2DMipMap();
		Texture->PlatformData->Mips.Add(Mip);
	}

	if (Texture)
	{
		Texture->PlatformData->Mips[0].SizeX = Width;
		Texture->PlatformData->Mips[0].SizeY = Height;

		Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
		
		uint8* RawData = static_cast<uint8*>(Texture->PlatformData->Mips[0].BulkData.Realloc(Pixels.Num() * GPixelFormats[EPixelFormat::PF_B8G8R8A8].BlockBytes));
		FMemory::Memcpy(RawData, Pixels.GetData(), Pixels.Num() * GPixelFormats[EPixelFormat::PF_B8G8R8A8].BlockBytes);
		
		Texture->PlatformData->Mips[0].BulkData.Unlock();
		
		Texture->UpdateResource();
	}
	
	return Texture;
}

void UFrameBuffer::DrawLine(int32 StartX, int32 StartY, int32 EndX, int32 EndY)
{
	// 如果斜率的绝对值大于1，求直线函数的反函数的直线
	const int32 Steep = (FMath::Abs(EndY - StartY) > abs(EndX - StartX)) ? 1 : 0;
	if (Steep == 1)
	{
		// 交换StartX，StartY坐标，得到反函数的起点坐标
		StartX ^= StartY;
		StartY ^= StartX;
		StartX ^= StartY;
            
		// 交换EndX, EndY坐标，得到反函数的终点坐标
		EndX ^= EndY;
		EndY ^= EndX;
		EndX ^= EndY;
	}
        
	// 如果起点坐标的x坐标在终点坐标的右边，则交换起点和终点坐标
	if (StartX > EndX)
	{
		StartX ^= EndX;
		EndX ^= StartX;
		StartX ^= EndX;
            
		StartY ^= EndY;
		EndY ^= StartY;
		StartY ^= EndY;
	}

	const int32 DX = FMath::Abs(EndX - StartX);     // x的微分
	const int32 DY = FMath::Abs(EndY - StartY);     // y的微分
        
	int32 P = (DY << 1) - DX;             // 中点判别式 p = 2 * dy - dx  

	const int32 Delta1 = DY << 1;         // p < 0 时的判别式的递推增量   delta1 = 2 * dy
	const int32 Delta2 = (DY - DX) << 1;  // p >= 0时的判别式的递推增量   delta2 = 2 * (dy - dx)

	const int32 Inc = (StartY < EndY) ? 1 : -1; // y坐标的每一次增长分量
	while (StartX <= EndX)
	{  
		if (Steep == 1)
		{
			Point(StartY,StartX);  // 绘制像素
		}
		else
		{
			Point(StartX, StartY);  
		}
            
		++StartX;  
          
		if (P<0)  
		{
			P += Delta1;  
		}
		else
		{  
			P += Delta2;  
			StartY += Inc;
		}
	}
}

/////////////////////////////////////////////////////
